package com.example.traning.mall.service.account.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.StrUtil;
import com.example.traning.mall.controller.account.vo.*;
import com.example.traning.mall.dal.dataobject.account.AccountLoginLog;
import com.example.traning.mall.dal.dataobject.account.AccountUser;
import com.example.traning.mall.dal.dataobject.account.AccountUserRole;
import com.example.traning.mall.dal.mysql.account.AccountLoginLogMapper;
import com.example.traning.mall.dal.mysql.account.AccountUserMapper;
import com.example.traning.mall.dal.mysql.account.AccountUserRoleMapper;
import com.example.traning.mall.dal.redis.account.AccountUserRedisDAO;
import com.example.traning.mall.dal.redis.account.UserCachePO;
import com.example.traning.mall.framework.common.enums.CommonStatusEnum;
import com.example.traning.mall.framework.common.ex.GlobalErrorCodeConstants;
import com.example.traning.mall.framework.common.pojo.PageParam;
import com.example.traning.mall.framework.common.pojo.PageResult;
import com.example.traning.mall.framework.common.util.json.JsonUtils;
import com.example.traning.mall.framework.common.util.servlet.ServletUtils;
import com.example.traning.mall.framework.security.core.util.JwtUtils;
import com.example.traning.mall.service.account.IAccountUserService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import static com.example.traning.mall.framework.common.consts.data.CommonConsts.ENABLE_STATE_ON;
import static com.example.traning.mall.framework.common.consts.web.JwtConsts.*;
import static com.example.traning.mall.framework.common.ex.GlobalErrorCodeConstants.*;
import static com.example.traning.mall.framework.common.ex.ServiceExceptionUtil.exception;
import static com.example.traning.mall.framework.common.util.collection.CollectionUtils.convertList;

/**
 * <p>
 * 用户 服务实现类
 * </p>
 *
 * @author Lei Yu
 * @since 2025-04-29
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class AccountUserServiceImpl implements IAccountUserService {

    private final PasswordEncoder passwordEncoder;
    private final AccountUserMapper accountUserMapper;
    private final AccountUserRoleMapper accountUserRoleMapper;
    private final AccountLoginLogMapper accountLoginLogMapper;
    private final AccountUserRedisDAO accountUserRedisDAO;
//    private final AccountUserConvert accountUserConvert;

    @Override
    public UserLoginResultVO login(UserLoginReqVO reqVO) {
        // 使用账号密码，进行登录
        UserLoginInfoVO infoVO = authenticate(reqVO.getUsername(), reqVO.getPassword());
        // 创建 Token 令牌，记录登录日志
        return createTokenAfterLoginSuccess(infoVO);
    }

    @Override
    public void logout(CurrentPrincipal currentPrincipal) {
        Long userId = currentPrincipal.getId();
        // 删除 redis 中登录信息
        Boolean deleteResult = accountUserRedisDAO.deleteUserState(userId);
        if (!deleteResult) {
            String message = "操作失败，用户数据有误！";
            log.warn(message);
            throw exception(GlobalErrorCodeConstants.NOT_FOUND.getCode(), message);
        }
    }

    @Override
    public void addNew(UserAddNewParam reqVO) {
        // 1. 校验正确性
        validateUserForCreateOrUpdate(null, reqVO.getUsername(), reqVO.getPhone(), reqVO.getEmail());
        // 2.1 插入用户
        AccountUser user = BeanUtil.toBean(reqVO, AccountUser.class);
        user.setLoginCount(0).setPassword(passwordEncoder.encode(reqVO.getPassword()));
        accountUserMapper.insert(user);
        if (ArrayUtil.isNotEmpty(reqVO.getRoleIds())) {

            List<AccountUserRole> roles = convertList(reqVO.getRoleIds(),
                    roleId -> new AccountUserRole().setUserId(user.getId()).setRoleId(roleId));
            accountUserRoleMapper.insertBatch(roles);
        }
    }

    @Override
    public void delete(Long id) {
        // 1. 校验用户存在
        validateUserExists(id);
        // 2.1 删除用户
        accountUserMapper.deleteById(id);
        // 2.2 删除用户角色关联
        accountUserRoleMapper.deleteByUserId(id);
    }

    @Override
    public void updateInfo(Long userId, UserUpdateInfoParam reqVO) {
        // 1. 校验用户存在
        validateUserExists(userId);
        AccountUser user = BeanUtil.copyProperties(reqVO, AccountUser.class);
        ;
        String newPassword = reqVO.getNewPassword();
        if (StringUtils.hasText(newPassword)) {
            user.setPassword(passwordEncoder.encode(newPassword));
        }
        accountUserMapper.updateById(user);
    }

    @Override
    public UserStandardVO getStandardById(Long userId) {
        AccountUser accountUser = accountUserMapper.selectById(userId);
        return BeanUtil.toBean(accountUser, UserStandardVO.class);
    }

    @Override
    public PageResult<AccountUser> selectPage(PageParam param) {
        PageResult<AccountUser> data = accountUserMapper.selectPage(param);
        return accountUserMapper.selectPage(param);
    }

    // =============== 校验参数方法 ===============

    private AccountUser validateUserForCreateOrUpdate(Long id, String username, String mobile, String email) {
        // 校验用户存在
        AccountUser user = validateUserExists(id);
        // 校验用户名唯一
        validateUsernameUnique(id, username);
        // 校验手机号唯一
        validateMobileUnique(id, mobile);
        // 校验邮箱唯一
        validateEmailUnique(id, email);
        return user;
    }

    AccountUser validateUserExists(Long id) {
        if (id == null) {
            return null;
        }
        AccountUser user = accountUserMapper.selectById(id);
        if (user == null) {
            throw exception(USER_NOT_EXISTS);
        }
        return user;
    }

    void validateUsernameUnique(Long id, String username) {
        if (StrUtil.isBlank(username)) {
            return;
        }
        AccountUser user = accountUserMapper.selectByUsername(username);
        if (user == null) {
            return;
        }
        // 如果 id 为空，说明不用比较是否为相同 id 的用户
        if (id == null) {
            throw exception(USER_USERNAME_EXISTS);
        }
        if (!user.getId().equals(id)) {
            throw exception(USER_USERNAME_EXISTS);
        }
    }

    void validateMobileUnique(Long id, String mobile) {
        if (StrUtil.isBlank(mobile)) {
            return;
        }
        AccountUser user = accountUserMapper.selectByMobile(mobile);
        if (user == null) {
            return;
        }
        // 如果 id 为空，说明不用比较是否为相同 id 的用户
        if (id == null) {
            throw exception(USER_MOBILE_EXISTS);
        }
        if (!user.getId().equals(id)) {
            throw exception(USER_MOBILE_EXISTS);
        }
    }

    void validateEmailUnique(Long id, String email) {
        if (StrUtil.isBlank(email)) {
            return;
        }
        AccountUser user = accountUserMapper.selectByEmail(email);
        if (user == null) {
            return;
        }
        // 如果 id 为空，说明不用比较是否为相同 id 的用户
        if (id == null) {
            throw exception(USER_EMAIL_EXISTS);
        }
        if (!user.getId().equals(id)) {
            throw exception(USER_EMAIL_EXISTS);
        }
    }


    // ================ 授权相关方法 ================

    private UserLoginInfoVO authenticate(String username, String password) {
        UserLoginInfoVO loginInfo = accountUserMapper.getLoginInfoByUsername(username);
        // 校验账号是否存在
        if (loginInfo == null) {
            throw exception(AUTH_LOGIN_BAD_CREDENTIALS);
        }
        // 校验密码
        if (!passwordEncoder.matches(password, loginInfo.getPassword())) {
            throw exception(AUTH_LOGIN_BAD_CREDENTIALS);
        }
        // 校验是否禁用
        if (CommonStatusEnum.isDisable(loginInfo.getEnable())) {
            throw exception(AUTH_LOGIN_USER_DISABLED);
        }
        return loginInfo;
    }

    private UserLoginResultVO createTokenAfterLoginSuccess(UserLoginInfoVO infoVO) {
        // 插入登陆日志
        createLoginLog(infoVO);
        // 创建访问令牌，并返回结果
        return createAccessToken(infoVO);

    }

    private void createLoginLog(UserLoginInfoVO vo) {
        String clientIp = ServletUtils.getClientIp();
        Long userId = vo.getId();
        LocalDateTime now = LocalDateTime.now();
        AccountLoginLog entity = new AccountLoginLog()
                .setUserId(userId).setUsername(vo.getUsername())
                .setIp(clientIp).setUserAgent(ServletUtils.getUserAgent()).setGmtLogin(now);
        // 插入登录日志
        accountLoginLogMapper.insert(entity);

        // 更新最后登录时间
        AccountUser updateUser = new AccountUser()
                .setId(userId).setLoginCount(vo.getLoginCount() + 1)
                .setLastLoginIp(clientIp).setGmtLastLogin(now);
        accountUserMapper.updateById(updateUser);
    }

    private UserLoginResultVO createAccessToken(UserLoginInfoVO vo) {
        Long userId = vo.getId();
        String username = vo.getUsername();
        // 生成 jwt
        Map<String, Object> claims = new HashMap<>();
        claims.put(CLAIM_USER_ID, userId);
        claims.put(CLAIM_USER_NAME, username);
        claims.put(CLAIM_USER_AGENT, ServletUtils.getUserAgent());
        claims.put(CLAIM_REMOTE_ADDR, ServletUtils.getClientIp());
        String jwt = JwtUtils.generate(claims);
        log.info("生成用户的JWT数据：{}", jwt);

        // 记录登录信息 到 redis
        List<String> permissions = vo.getPermissions();
        String authoritiesJsonString = JsonUtils.toJsonString(permissions);
        UserCachePO po = new UserCachePO().setEnable(ENABLE_STATE_ON).setAuthoritiesJsonString(authoritiesJsonString);
        accountUserRedisDAO.saveUserState(userId, po);


        return new UserLoginResultVO().setId(userId).setUsername(username)
                .setAvatar(vo.getAvatar()).setToken(jwt).setAuthorities(permissions);
    }
}
